﻿using System;
using System.Collections.Generic;
using System.Data; //DataSet, DataTable, DataRow
using System.Data.OleDb; //OleDbDataAdapter

namespace Hausaufgabe1
{
    /// <summary>
    /// Zugriff auf die Datenbank.
    /// </summary>
    class Datenbank
    {
        //Gesamter Spiegel.
        private DataSet dataSet;
        //Gespiegelte Tabellen.
        private DataTable tabelleAutoren;
        private DataTable tabelleBuecher;
        //Tabellenadapter.
        private OleDbDataAdapter adapterAutoren;
        private OleDbDataAdapter adapterBuecher;
        
        /* Ausgeschaltet, da mit der DataRelation jegliche Inserts verweigert werden.
        //Fremdschlüssel.
        private DataRelation zuordnungBuecher;
        */

        /// <summary>
        /// Konstruktor mit allen Einstellungen und Eigenschaften.
        /// </summary>
        public Datenbank()
        {
            //Datenbankort.
            string datenbankOrt = @"C:\Users\Spam\Desktop\Buchliste.mdb";

            //Connctionstring.
            string connectionString = @"provider=Microsoft.Jet.OLEDB.4.0; data source = " + datenbankOrt;

            //Tabellen spiegeln.
            this.adapterAutoren = new OleDbDataAdapter("SELECT * FROM Autoren;", connectionString);
            this.adapterBuecher = new OleDbDataAdapter("SELECT * FROM Buecher;", connectionString);

            //Leeres Select-Commando der Verbindung.
            this.adapterAutoren.InsertCommand = this.adapterAutoren.SelectCommand.Connection.CreateCommand();
            this.adapterAutoren.InsertCommand.CommandText = "INSERT INTO Autoren (ID, Nachname, Vorname) VALUES (@ID, @Nachname, @Vorname);";
            this.adapterAutoren.InsertCommand.Parameters.Add("@ID", OleDbType.Integer, 0, "ID");
            this.adapterAutoren.InsertCommand.Parameters.Add("@Nachname", OleDbType.Char, 255, "Nachname");
            this.adapterAutoren.InsertCommand.Parameters.Add("@Vorname", OleDbType.Char, 255, "Vorname");

            this.adapterBuecher.InsertCommand = this.adapterBuecher.SelectCommand.Connection.CreateCommand();
            this.adapterBuecher.InsertCommand.CommandText = "INSERT INTO Buecher (ID, Titel, Verlag, Jahr) VALUES (@ID, @Titel, @Verlag, @Jahr);";
            this.adapterBuecher.InsertCommand.Parameters.Add("@ID", OleDbType.Integer, 0, "ID");
            this.adapterBuecher.InsertCommand.Parameters.Add("@Titel", OleDbType.Char, 255, "Titel");
            this.adapterBuecher.InsertCommand.Parameters.Add("@Verlag", OleDbType.Char, 255, "Verlag");
            this.adapterBuecher.InsertCommand.Parameters.Add("@Jahr", OleDbType.Integer, 0, "Jahr");

            //Speicherplatz für Spiegel.
            this.dataSet = new DataSet();

            //Tabellen in den Hauptspeicher laden.
            try
            {
                this.adapterAutoren.Fill(this.dataSet, "Autoren");
                this.adapterBuecher.Fill(this.dataSet, "Buecher");
            }
            catch
            {
                throw new Exception("Laden der Datenbank nicht möglich.");
            }

            //Tabellen mit Tabellenobjekt verknüpfen.
            this.tabelleAutoren = this.dataSet.Tables["Autoren"];
            this.tabelleBuecher = this.dataSet.Tables["Buecher"];

            //Primärschlüssel.
            DataColumn[] primaryAutoren = new DataColumn[1];
            primaryAutoren[0] = tabelleAutoren.Columns["ID"];
            this.tabelleAutoren.PrimaryKey = primaryAutoren;

            DataColumn[] primaryBuecher = new DataColumn[1];
            primaryBuecher[0] = tabelleBuecher.Columns["ID"];
            this.tabelleBuecher.PrimaryKey = primaryBuecher;

            //Relation.
            //this.zuordnungBuecher = this.dataSet.Relations.Add("ZuordnungAutoren", this.tabelleAutoren.Columns["ID"], this.tabelleBuecher.Columns["ID"]);
        }

        /// <summary>
        /// Anzahl Autoren.
        /// </summary>
        public int AnzahlAutoren
        {
            get { return this.tabelleAutoren.Rows.Count; }
        }
        /// <summary>
        /// Anzahl Buecher.
        /// </summary>
        public int AnzahlBuecher
        {
            get { return this.tabelleBuecher.Rows.Count; }
        }

        /// <summary>
        /// Autorenliste.
        /// </summary>
        /// <returns>Liste von Autoren.</returns>
        public List<Person> Autoren()
        {
            //Collector für Autoren.
            List<Person> autoren = new List<Person>();

            //Autoren einzeln durchgehen.
            foreach (DataRow autor in this.tabelleAutoren.Rows)
            {
                Person person = new Person();
                person.Nachname = (string)autor["Nachname"];
                person.Vorname = (string)autor["Vorname"];

                autoren.Add(person);
            }

            //Rückgabe.
            return autoren;
        }

        /// <summary>
        /// Buecherliste eines angegebenen Autors.
        /// </summary>
        /// <param name="autor">Autor</param>
        /// <returns>Liste von Büchern von angegebenem Autor.</returns>
        public List<Buch> Buecher(Person autor)
        {
            //Collector für Bücher.
            List<Buch> buecher = new List<Buch>();

            //Bücher einzeln durchgehen.
            foreach (DataRow buch_einzeln in this.tabelleBuecher.Rows)
            {
                Person autor_db = Autor((int)buch_einzeln["Autor"]);

                if (autor_db.Nachname == autor.Nachname && autor_db.Vorname == autor.Vorname)
                {
                    //Buch zusammenstellen.
                    Buch buch = new Buch();
                    buch.Autor = autor;
                    buch.Erscheinungsjahr = (int)buch_einzeln["Jahr"];
                    buch.Titel = (string)buch_einzeln["Titel"];
                    buch.Verlag = (string)buch_einzeln["Verlag"];

                    //Buch zur Sammlung.
                    buecher.Add(buch);
                }
            }

            //Bücher zurückgeben.
            return buecher;
        }

        /// <summary>
        /// Buch anlegen.
        /// </summary>
        /// <param name="nachname">Nachname des Autors.</param>
        /// <param name="vorname">Vorname des Autors.</param>
        /// <param name="titel">Buchtitel.</param>
        /// <param name="verlag">Buchverlag.</param>
        /// <param name="jahr">Erscheinungsjahr des Buches.</param>
        public void BuchAnlegen(string nachname, string vorname, string titel, string verlag, int jahr)
        {
            //Argumente checken.
            if (nachname == "")
            {
                throw new Exception("Es wurde kein Nachname angegeben!");
            }
            if (vorname == "")
            {
                throw new Exception("Es wurde kein Vorname angegeben!");
            }
            if (titel == "")
            {
                throw new Exception("Es wurde kein Titel angegeben!");
            }
            if (verlag == "")
            {
                throw new Exception("Es wurde kein Verlag angegeben!");
            }

            int autor_id = AutorExistent(nachname, vorname);
            if (autor_id == 0)
            {
                //Neuen Autor erstellen.
                DataRow autor = this.tabelleAutoren.NewRow();
                autor["ID"] = AnzahlAutoren + 1;
                autor["Nachname"] = nachname;
                autor["Vorname"] = vorname;

                //Autor zum Spiegel hinzufügen.
                this.tabelleAutoren.Rows.Add(autor);

                //Spiegel updaten.
                Update(this.adapterAutoren, this.tabelleAutoren);
            }

            //Neues Buch erstellen.
            DataRow buch = this.tabelleBuecher.NewRow();
            buch["ID"] = AnzahlBuecher + 1;
            buch["Titel"] = titel;
            buch["Verlag"] = verlag;
            buch["Jahr"] = jahr;
            autor_id = AutorExistent(nachname, vorname);
            buch["Autor"] = autor_id;

            //Buch zum Spiegel hinzufügen.
            this.tabelleBuecher.Rows.Add(buch);

            //Spiegel updaten.
            Update(this.adapterBuecher, this.tabelleBuecher);
        }

        /// <summary>
        /// Änderungen in die Datenbank übernehmen.
        /// </summary>
        /// <param name="adapter">Adapter der Tabelle.</param>
        /// <param name="tabelle">Spiegeltabelle.</param>
        /// <returns>Rückmeldung über Erfolg.</returns>
        private bool Update(OleDbDataAdapter adapter, DataTable tabelle)
        {
            bool erfolg = false;

            try
            {
                //Änderungen vom Spiegel auf Datenbank übertragen.
                adapter.Update(tabelle);
                //Änderungen als übernommen markieren.
                tabelle.AcceptChanges();

                erfolg = true;
            }
            catch (Exception ex)
            {
                //Änderungen im Spiegel verwerfen.
                tabelle.RejectChanges();
                throw new Exception("Fehler: " + ex.Message);
            }

            return erfolg;
        }

        /// <summary>
        /// Prüfen, ob Autor bereits existiert.
        /// </summary>
        /// <param name="nachname">Nachname des Autors.</param>
        /// <param name="vorname">Vorname des Autors.</param>
        /// <returns>ID des Autors oder 0 für keinen Autor.</returns>
        private int AutorExistent(string nachname, string vorname)
        {
            int autor_id = 0;

            //Alle Autoren durchgehen und nach Vorhandenheit überprüfen.
            foreach (DataRow autor in this.tabelleAutoren.Rows)
            {
                if (nachname == (string)autor["Nachname"] && vorname == (string)autor["Vorname"])
                {
                    autor_id = (int)autor["ID"];
                    break;
                }
            }

            return autor_id;
        }

        /// <summary>
        /// Einzelnen Autor holen.
        /// </summary>
        /// <param name="id">ID des Autors.</param>
        /// <returns>Gefundener Autor oder Null für keinen Autor.</returns>
        private Person Autor(int id)
        {
            //Autoren einzeln durchgehen.
            foreach (DataRow autor in this.tabelleAutoren.Rows)
            {
                int autor_id = (int)autor["ID"];
                if (id == autor_id)
                {
                    Person p = new Person();
                    p.Nachname = (string)autor["Nachname"];
                    p.Vorname = (string)autor["Vorname"];
                    return p;
                }
            }

            //Fall kein Autor vorhanden.
            return null;
        }
    }
}